﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Globalization;
using AqueleAjax.UI;

namespace AqueleAjax.UI.Controls
{
    /// <summary>
    /// Este controle controla a execução e configuração do Ajax. Cada página deve conter apenas um AjaxMainControl.
    /// </summary>
    public class AjaxMainControl : Control
    {
        /// <summary>
        /// Nome do campo oculto no HTML que armazena os dados da ViewState.
        /// </summary>
        private const string ViewStateFieldID = "__VIEWSTATE";

        private Hashtable _htmlControls;

        /// <summary>
        /// Controles que será renderizados durante uma chamada assíncrona.
        /// </summary>
        private Hashtable HtmlControls
        {
            get
            {
                if (this._htmlControls == null)
                {
                    this._htmlControls = new Hashtable();
                }

                return _htmlControls;
            }
        }

        private ClientScript _clientScript;

        /// <summary>
        /// Gerencia os scripts gerados e enviados ao cliente.
        /// </summary>
        public ClientScript ClientScript
        {
            get
            {
                if (this._clientScript == null)
                {
                    this._clientScript = new ClientScript(this);
                }

                return this._clientScript; 
            }
            private set
            {
                this._clientScript = value; 
            }
        }

        /// <summary>
        /// Atualiza o HTML do controle no retorno da chamada assíncrona.
        /// </summary>
        /// <param name="control">Controle que será atualizado.</param>
        public virtual void Update(Control control)
        {
            if (control == null) throw new ArgumentNullException("control");

            this.RenderControl(control);
        }

        /// <summary>
        /// Obtem o resultado da chamada assíncrona. O conteúdo é um texto contendo instruções JavaScript.
        /// </summary>
        /// <returns>Conteúdo é um texto contendo instruções JavaScript.</returns>
        public virtual string GetCallbackResult()
        {
            StringBuilder script = new StringBuilder();

            foreach (DictionaryEntry item in this.HtmlControls)
            {
                script.AppendFormat("___aqueleAjax.AlterHtmlElement('{0}', '{1}');", item.Key, item.Value);
            }

            foreach (DictionaryEntry item in this.ClientScript.Scripts)
            {
                script.AppendFormat("{0};", item.Value);
            }

            script.AppendFormat("___aqueleAjax.AlterValueElement('{0}', '{1}');", AjaxMainControl.ViewStateFieldID, this.GetUpdatedViewState());

            return script.ToString();
        }

        public static string CallbackEventReference(Control control)
        {
            string scriptCallback = control.Page.ClientScript.GetCallbackEventReference(
                control,
                string.Format(CultureInfo.CurrentCulture, "'{0};'+ ___aqueleAjax.GetElementValue('{1}')", control.ClientID, control.ClientID),
                ClientScript.ResultCallbackFunction,
                string.Format(CultureInfo.CurrentCulture, "'{0}'", control.ClientID),
                ClientScript.ErrorCallbackFunction,
                true
                );

            return string.Format(CultureInfo.CurrentCulture, "___aqueleAjax.RaiseCallback(\"{0}\");", scriptCallback);
        }

        public static AjaxMainControl GetAjaxMainControl(Control control)
        {
            if (control == null) return null;

            AjaxMainControl result = control as AjaxMainControl;

            if (result == null)
            {
                for (int i = 0; i < control.Controls.Count; i++)
                {
                    result = AjaxMainControl.GetAjaxMainControl(control.Controls[i]);

                    if (result != null)
                    {
                        break;
                    }
                }
            }

            return result;
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            this.ClientScript = new ClientScript(this);
        }

        private void RenderControl(Control control)
        {
            StringWriter writer = null;
            HtmlTextWriter html = null;
            System.Reflection.MethodInfo method = null;

            try
            {
                writer = new StringWriter(CultureInfo.CurrentCulture);
                html = new HtmlTextWriter(writer);

                method = typeof(Control).GetMethod("Render", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                method.Invoke(control, new object[] { html });

                this.HtmlControls.Add(control.ClientID, AjaxUtil.ClearHtml(html.InnerWriter.ToString()));
            }
            finally
            {
                if (writer != null)
                {
                    writer.Flush();
                    writer.Close();
                    writer.Dispose();
                    writer = null;
                }

                if (html != null)
                {
                    html.Flush();
                    html.Close();
                    html.Dispose();
                    html = null;
                }
            }
        }

        private string GetUpdatedViewState()
        {
            System.Reflection.MethodInfo mi = typeof(Page).GetMethod("SaveAllState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            System.Reflection.PropertyInfo stateProp = typeof(Page).GetProperty("ClientState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

            mi.Invoke(this.Page, null);

            return stateProp.GetValue(this.Page, null).ToString();
        }
    }
}
